/**********************************************************************

  NyqBench.cpp

  Leland Lucius

**********************************************************************/

#include <wx/defs.h>

#include <wx/aboutdlg.h>
#include <wx/filedlg.h>
#include <wx/font.h>
#include <wx/fontdlg.h>
#include <wx/menu.h>
#include <wx/msgdlg.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/splitter.h>
#include <wx/statbox.h>
#include <wx/textctrl.h>
#include <wx/toolbar.h>

#include "ActiveProject.h"
#include "AudioIOBase.h"
#include "CommonCommandFlags.h"
#include "ModuleConstants.h"
#include "Prefs.h"
#include "Project.h"
#include "ShuttleGui.h"
#include "EffectManager.h"
#include "effects/EffectUI.h"
#include "effects/nyquist/Nyquist.h"
#include "../images/AudacityLogo.xpm"
#include "CommandContext.h"
#include "AudacityMessageBox.h"

#include "NyqBench.h"

#include <iostream>
#include <ostream>
#include <sstream>

//
// Images are from the Tango Icon Gallery
// http://tango.freedesktop.org/Tango_Icon_Gallery
//
#include "images/document-new-small.xpm"
#include "images/document-open-small.xpm"
#include "images/document-save-as-small.xpm"
#include "images/document-save-small.xpm"
#include "images/edit-clear-small.xpm"
#include "images/edit-copy-small.xpm"
#include "images/edit-cut-small.xpm"
#include "images/edit-delete-small.xpm"
#include "images/edit-find-small.xpm"
#include "images/edit-paste-small.xpm"
#include "images/edit-redo-small.xpm"
#include "images/edit-select-all-small.xpm"
#include "images/edit-undo-small.xpm"
#include "images/go-top-small.xpm"
#include "images/go-up-small.xpm"
#include "images/go-previous-small.xpm"
#include "images/go-next-small.xpm"
#include "images/system-search-small.xpm"
#include "images/media-playback-start-small.xpm"
#include "images/media-playback-stop-small.xpm"

#include "images/document-new-large.xpm"
#include "images/document-open-large.xpm"
#include "images/document-save-as-large.xpm"
#include "images/document-save-large.xpm"
#include "images/edit-clear-large.xpm"
#include "images/edit-copy-large.xpm"
#include "images/edit-cut-large.xpm"
#include "images/edit-delete-large.xpm"
#include "images/edit-find-large.xpm"
#include "images/edit-paste-large.xpm"
#include "images/edit-redo-large.xpm"
#include "images/edit-select-all-large.xpm"
#include "images/edit-undo-large.xpm"
#include "images/go-top-large.xpm"
#include "images/go-up-large.xpm"
#include "images/go-previous-large.xpm"
#include "images/go-next-large.xpm"
#include "images/system-search-large.xpm"
#include "images/media-playback-start-large.xpm"
#include "images/media-playback-stop-large.xpm"

/*
//#define ModuleDispatchName "ModuleDispatch"
See the example in this file.  It has several cases/options in it.
*/

namespace {
CommandHandlerObject& findme(AudacityProject&)
{
    return *NyqBench::GetBench();
}

void RegisterMenuItems()
{
    // Get here only after the module version check passes
    using namespace MenuRegistry;
    static AttachedItem sAttachment{ wxT("Tools"),
                                     (FinderScope(findme), Section(wxT("NyquistWorkBench"),
                                                                   Command(wxT("NyqBench"), XXO("&Nyquist Workbench..."),
                                                                           &NyqBench::ShowNyqBench, AudioIONotBusyFlag())
                                                                   ))
    };
}
}

DEFINE_VERSION_CHECK

extern "C"
{
static NyqBench* gBench = NULL;

extern int DLL_API ModuleDispatch(ModuleDispatchTypes type);
// ModuleDispatch
// is called by Audacity to initialize/terminate the module
int ModuleDispatch(ModuleDispatchTypes type)
{
    switch (type) {
    case ModuleInitialize:
        RegisterMenuItems();
        break;
    case AppQuiting: {
        //It is perfectly OK for gBench to be NULL.
        //Can happen if the menu item was never invoked.
        //wxASSERT(gBench != NULL);
        if (gBench) {
            // be sure to do this while gPrefs still exists:
            gBench->SavePrefs();
            gBench->Destroy();
            gBench = NULL;
        }
    }
    break;
    default:
        break;
    }
    return 1;
}
};

//----------------------------------------------------------------------------
// NyqTextCtrl
//----------------------------------------------------------------------------

BEGIN_EVENT_TABLE(NyqTextCtrl, wxTextCtrl)
#if defined(__WXMAC__)
EVT_KEY_DOWN(NyqTextCtrl::OnKeyDown)
#endif
EVT_KEY_UP(NyqTextCtrl::OnKeyUp)
EVT_CHAR(NyqTextCtrl::OnChar)
EVT_UPDATE_UI(wxID_ANY, NyqTextCtrl::OnUpdate)
END_EVENT_TABLE()

NyqTextCtrl::NyqTextCtrl(wxWindow* parent,
                         wxWindowID id,
                         const wxString& value,
                         const wxPoint& pos,
                         const wxSize& size,
                         int style)
    :  wxTextCtrl(parent, id, value, pos, size, style)
{
    mLastCaretPos = -1;
    mLeftParen = -1;
    mRightParen = -1;

    mOn.SetTextColour(*wxRED);
    mOff.SetTextColour(*wxBLACK);
}

void NyqTextCtrl::SetFocusFromKbd()
{
#if defined(__WXMSW__)
    // We do this to prevent wxMSW from selecting all text when the
    // user tabs into the text controls.
    wxWindowBase::SetFocusFromKbd();
#else
    wxTextCtrl::SetFocusFromKbd();
#endif
}

void NyqTextCtrl::MarkDirty()
{
    wxTextCtrl::MarkDirty();
    FindParens();
}

void NyqTextCtrl::OnChar(wxKeyEvent& e)
{
    e.Skip();

    // Hide any previously highlighted parens
    if (mLeftParen >= 0) {
#if defined(__WXMSW__)
        Freeze(); // Prevents selection flashing on Windows
#endif

        SetStyle(mLeftParen, mLeftParen + 1, mOff);
        SetStyle(mLeftParens[mLeftParen], mLeftParens[mLeftParen] + 1, mOff);
        mLeftParen = -1;
        mRightParen = -1;

#if defined(__WXMSW__)
        Thaw(); // Prevents selection flashing on Windows
#endif
    }
}

#if defined(__WXMAC__REMOVED_UNTIL_ITS_PROVEN_THAT_IT_IS_STILL_NEEDED)
#include <wx/mac/uma.h>

// This is hackage to correct a problem on Leopard where the
// caret jumps up two lines when the up arrow is pressed and
// the caret is at the beginning of the line.
void NyqTextCtrl::OnKeyDown(wxKeyEvent& e)
{
    e.Skip();
    if (UMAGetSystemVersion() >= 0x1050) {
        if (e.GetKeyCode() == WXK_UP && e.GetModifiers() == 0) {
            long x;
            long y;

            PositionToXY(GetInsertionPoint(), &x, &y);
            if (x == 0 && y > 1) {
                y--;
                SetInsertionPoint(XYToPosition(x, y) - 1);
                e.Skip(false);
            }
        }
    }
}

#endif

void NyqTextCtrl::OnKeyUp(wxKeyEvent& e)
{
    e.Skip();

    int pos = GetInsertionPoint();
    int lpos = wxMax(0, pos - 1);

    wxString text = GetRange(lpos, pos);

    if (text[0] == wxT('(')) {
        wxLongToLongHashMap::const_iterator left = mLeftParens.find(lpos);
        if (left != mLeftParens.end()) {
            Colorize(lpos, left->second);
        }
    } else if (text[0] == wxT(')')) {
        wxLongToLongHashMap::const_iterator right = mRightParens.find(lpos);
        if (right != mRightParens.end()) {
            Colorize(right->second, lpos);
        }
    }
}

void NyqTextCtrl::OnUpdate(wxUpdateUIEvent& e)
{
    int pos = GetInsertionPoint();

    if (pos != mLastCaretPos) {
        int lpos = wxMax(0, pos - 1);

        wxString text = GetRange(lpos, pos);
        if (text.Length() > 0) {
            if (text[0] == wxT('(')) {
                wxLongToLongHashMap::const_iterator left = mLeftParens.find(lpos);
                if (left != mLeftParens.end()) {
                    Colorize(lpos, left->second);
                }
            } else if (text[0] == wxT(')')) {
                wxLongToLongHashMap::const_iterator right = mRightParens.find(lpos);
                if (right != mRightParens.end()) {
                    Colorize(right->second, lpos);
                }
            }
        }

        mLastCaretPos = pos;
    }
}

void NyqTextCtrl::GoMatch()
{
    MoveCursor(mRightParen, mLeftParen);
}

void NyqTextCtrl::GoTop()
{
    wxLongToLongHashMap::const_iterator it;
    long first = -1;
    long second = -1;

    if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
            if (mLeftParen > it->first && mLeftParen < it->second) {
                if (first == -1 || it->first < first) {
                    first = it->first;
                    second = it->second;
                }
            }
        }
    }

    if (first != -1) {
        MoveCursor(first, second);
    }
}

void NyqTextCtrl::GoUp()
{
    wxLongToLongHashMap::const_iterator it;
    long first = -1;
    long second = -1;

    if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
            if (mLeftParen > it->first && mLeftParen < it->second) {
                if (first == -1 || it->first > first) {
                    first = it->first;
                    second = it->second;
                }
            }
        }
    }

    if (first != -1) {
        MoveCursor(first, second);
    }
}

void NyqTextCtrl::GoPrev()
{
    wxLongToLongHashMap::const_iterator it;
    long first = -1;
    long second = -1;

    if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
            if (it->first < mLeftParen && it->first >= first) {
                first = it->first;
                second = it->second;
            }
        }
    }

    if (first != -1) {
        MoveCursor(first, second);
    }
}

void NyqTextCtrl::GoNext()
{
    wxLongToLongHashMap::const_iterator it;
    long first = -1;
    long second = -1;

    if (mLeftParen != -1) {
        for (it = mLeftParens.begin(); it != mLeftParens.end(); it++) {
            if (it->first > mLeftParen && (first == -1 || it->first < first)) {
                first = it->first;
                second = it->second;
            }
        }
    }

    if (first != -1) {
        MoveCursor(first, second);
    }
}

void NyqTextCtrl::MoveCursor(long first, long second)
{
    int pos = GetInsertionPoint();
    int lpos = wxMax(0, pos - 1);

    wxString text = GetRange(lpos, pos);

    if (text[0] == wxT('(')) {
        SetInsertionPoint(first + 1);
        Colorize(first, second);
    } else if (text[0] == wxT(')')) {
        SetInsertionPoint(second + 1);
        Colorize(first, second);
    }
}

void NyqTextCtrl::Colorize(long left, long right)
{
    // Hide any previously highlighted parens
    if (mLeftParen >= 0) {
#if defined(__WXMSW__)
        Freeze(); // Prevents selection flashing on Windows
#endif

        SetStyle(mLeftParen, mLeftParen + 1, mOff);
        SetStyle(mLeftParens[mLeftParen], mLeftParens[mLeftParen] + 1, mOff);
        mLeftParen = -1;
        mRightParen = -1;

#if defined(__WXMSW__)
        Thaw(); // Prevents selection flashing on Windows
#endif
    }

    mLeftParen = left;
    mRightParen = right;

    if (mLeftParen != -1) {
        SetStyle(mLeftParen, mLeftParen + 1, mOn);
        SetStyle(mRightParen, mRightParen + 1, mOn);

        SetStyle(mLeftParen + 1, mLeftParen + 1, mOff);
        SetStyle(mRightParen + 1, mRightParen + 1, mOff);
    }
}

void NyqTextCtrl::FindParens()
{
    wxString text = GetValue();
    bool inquotes = false;
    wxArrayInt stack;
    long len = (long)text.Length();
    long pos;

    mLeftParens.clear();
    mRightParens.clear();

    for (pos = 0; pos < len; pos++) {
        wxChar c = text[pos];
        switch (c) {
        case wxT('"'):
            inquotes = !inquotes;
            break;

        case wxT(';'):
            if (!inquotes) {
                pos = (long)text.find(wxT('\n'), pos);
                if (pos == (long)wxString::npos) {
                    pos = len;
                }
            }
            break;

        case wxT('#'):
            if (!inquotes) {
                long ndx = pos + 1;
                if (ndx < len && text[(int)ndx] == wxT('|')) {
                    // Shamelessly stolen from xlread.c/pcomment()
                    wxChar lastch = -1;
                    int n = 1;

                    /* look for the matching delimiter (and handle nesting) */
                    while (n > 0 && ++pos < len) {
                        wxChar ch = text[(int)pos];
                        if (lastch == '|' && ch == '#') {
                            --n;
                            ch = -1;
                        } else if (lastch == '#' && ch == '|') {
                            ++n;
                            ch = -1;
                        }
                        lastch = ch;
                    }
                }
            }
            break;

        case wxT('('):
            if (!inquotes) {
                stack.Add(pos);
            }
            break;

        case wxT(')'):
            if (!inquotes) {
                if (stack.GetCount() > 0) {
                    int left = stack.Last();
                    stack.RemoveAt(stack.GetCount() - 1);

                    mLeftParens[left] = pos;
                    mRightParens[pos] = left;
                }
            }
            break;
        }
    }
}

//----------------------------------------------------------------------------
// NyqRedirector
//----------------------------------------------------------------------------

NyqRedirector::NyqRedirector(NyqTextCtrl* text)
    :  mText(text)
{
    mOld = std::cout.rdbuf(this);
}

NyqRedirector::~NyqRedirector()
{
    std::cout.flush();
    std::cout.rdbuf(mOld);
    if (s.length() > 0) {
        AppendText();
    }
}

int NyqRedirector::overflow(int c)
{
    s += (char)c;
    if (c == '\n') {
        AppendText();
    }

    return 0;
}

void NyqRedirector::AppendText()
{
    mText->AppendText(wxString(s.c_str(), wxConvISO8859_1));
    s.clear();
}

//----------------------------------------------------------------------------
// NyqBench
//----------------------------------------------------------------------------

enum
{
    ID_AUTOLOAD = 20000,

    ID_AUTOWRAP,

    ID_FONT,
    ID_SPLITV,
    ID_SPLITH,
    ID_TOGGLECODE,
    ID_TOGGLEOUTPUT,
    ID_SMALLICONS,
    ID_LARGEICONS,
    ID_MATCH,
    ID_TOP,
    ID_UP,
    ID_PREVIOUS,
    ID_NEXT,

    ID_GO,
    ID_STOP,

    ID_SCRIPT,
    ID_OUTPUT
};

BEGIN_EVENT_TABLE(NyqBench, wxFrame)
EVT_CLOSE(NyqBench::OnClose)
EVT_MOVE(NyqBench::OnMove)
EVT_SIZE(NyqBench::OnSize)

EVT_MENU(wxID_NEW, NyqBench::OnNew)
EVT_MENU(wxID_OPEN, NyqBench::OnOpen)
EVT_MENU(wxID_SAVE, NyqBench::OnSave)
EVT_MENU(wxID_SAVEAS, NyqBench::OnSaveAs)
EVT_MENU(wxID_REVERT_TO_SAVED, NyqBench::OnRevert)
EVT_MENU(ID_AUTOLOAD, NyqBench::OnAutoLoad)
EVT_MENU(wxID_CLOSE, NyqBench::OnCloseWindow)

EVT_MENU(wxID_UNDO, NyqBench::OnUndo)
EVT_MENU(wxID_REDO, NyqBench::OnRedo)
EVT_MENU(wxID_CUT, NyqBench::OnCut)
EVT_MENU(wxID_COPY, NyqBench::OnCopy)
EVT_MENU(wxID_PASTE, NyqBench::OnPaste)
EVT_MENU(wxID_CLEAR, NyqBench::OnClear)
EVT_MENU(wxID_SELECTALL, NyqBench::OnSelectAll)
EVT_MENU(wxID_FIND, NyqBench::OnFind)
EVT_MENU(ID_MATCH, NyqBench::OnGoMatch)
EVT_MENU(ID_TOP, NyqBench::OnGoTop)
EVT_MENU(ID_UP, NyqBench::OnGoUp)
EVT_MENU(ID_PREVIOUS, NyqBench::OnGoPrev)
EVT_MENU(ID_NEXT, NyqBench::OnGoNext)
EVT_MENU(ID_AUTOWRAP, NyqBench::OnAutoWrap)

EVT_MENU(ID_FONT, NyqBench::OnFont)
EVT_MENU(ID_SPLITV, NyqBench::OnSplitV)
EVT_MENU(ID_SPLITH, NyqBench::OnSplitH)
EVT_MENU(ID_TOGGLECODE, NyqBench::OnToggleCode)
EVT_MENU(ID_TOGGLEOUTPUT, NyqBench::OnToggleOutput)
EVT_MENU(ID_SMALLICONS, NyqBench::OnSmallIcons)
EVT_MENU(ID_LARGEICONS, NyqBench::OnLargeIcons)

EVT_MENU(ID_GO, NyqBench::OnGo)
EVT_MENU(ID_STOP, NyqBench::OnStop)

EVT_MENU(wxID_ABOUT, NyqBench::OnAbout)

EVT_FIND(wxID_ANY, NyqBench::OnFindDialog)
EVT_FIND_NEXT(wxID_ANY, NyqBench::OnFindDialog)
EVT_FIND_REPLACE(wxID_ANY, NyqBench::OnFindDialog)
EVT_FIND_REPLACE_ALL(wxID_ANY, NyqBench::OnFindDialog)
EVT_FIND_CLOSE(wxID_ANY, NyqBench::OnFindDialog)

EVT_TEXT(ID_SCRIPT, NyqBench::OnTextUpdate)

EVT_UPDATE_UI(wxID_SAVE, NyqBench::OnMenuUpdate)
EVT_UPDATE_UI(wxID_SAVEAS, NyqBench::OnMenuUpdate)
EVT_UPDATE_UI(wxID_REVERT_TO_SAVED, NyqBench::OnMenuUpdate)

EVT_UPDATE_UI(wxID_UNDO, NyqBench::OnUndoUpdate)
EVT_UPDATE_UI(wxID_REDO, NyqBench::OnRedoUpdate)
EVT_UPDATE_UI(wxID_CUT, NyqBench::OnCutUpdate)
EVT_UPDATE_UI(wxID_COPY, NyqBench::OnCopyUpdate)
EVT_UPDATE_UI(wxID_PASTE, NyqBench::OnPasteUpdate)
EVT_UPDATE_UI(wxID_CLEAR, NyqBench::OnClearUpdate)

EVT_UPDATE_UI(ID_SPLITH, NyqBench::OnViewUpdate)
EVT_UPDATE_UI(ID_SPLITV, NyqBench::OnViewUpdate)
EVT_UPDATE_UI(ID_TOGGLECODE, NyqBench::OnViewUpdate)
EVT_UPDATE_UI(ID_TOGGLEOUTPUT, NyqBench::OnViewUpdate)

EVT_UPDATE_UI(ID_GO, NyqBench::OnRunUpdate)

EVT_UPDATE_UI(ID_SCRIPT, NyqBench::OnScriptUpdate)
EVT_UPDATE_UI(ID_OUTPUT, NyqBench::OnOutputUpdate)
END_EVENT_TABLE()

/*static*/ NyqBench* NyqBench::GetBench()
{
    if (gBench == nullptr) {
        gBench = new NyqBench(NULL);
    }

    return gBench;
}

NyqBench::NyqBench(wxWindow* parent)
    :  wxFrame(NULL,
               wxID_ANY,
               wxEmptyString,
               wxDefaultPosition,
               wxDefaultSize,
               wxDEFAULT_FRAME_STYLE
               | wxMINIMIZE_BOX
               | wxMAXIMIZE_BOX
               | wxRESIZE_BORDER)
{
    mFindDlg = NULL;
    mRunning = false;
    mScriptBox = NULL;
    mOutputBox = NULL;
    mScript = NULL;
    mOutput = NULL;

    mPath = gPrefs->Read(wxT("NyqBench/Path"), wxEmptyString);
    mAutoLoad = (gPrefs->Read(wxT("NyqBench/AutoLoad"), 0L) != 0);
    mAutoWrap = (gPrefs->Read(wxT("NyqBench/AutoWrap"), true) != 0);
    mLargeIcons = (gPrefs->Read(wxT("NyqBench/LargeIcons"), 0L) != 0);
    mSplitMode = gPrefs->Read(wxT("NyqBench/SplitMode"), wxSPLIT_VERTICAL);
    mShowCode = (gPrefs->Read(wxT("NyqBench/ShowScript"), true) != 0);
    mShowOutput = (gPrefs->Read(wxT("NyqBench/ShowOutput"), true) != 0);

    SetIcon(wxICON(AudacityLogo));
    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
    ShuttleGui S(this, eIsCreating);
    PopulateOrExchange(S);
    wxMenuBar* bar = new wxMenuBar();

    wxMenu* menu = new wxMenu();
    menu->Append(wxID_NEW, wxT("&New\tCtrl+N"));
    menu->Append(wxID_OPEN, wxT("&Open...\tCtrl+O"));
    menu->Append(wxID_SAVE, wxT("&Save...\tCtrl+S"));
    menu->Append(wxID_SAVEAS, wxT("Save &As...\tCtrl+Shift+S"));
    menu->AppendSeparator();
    menu->Append(wxID_REVERT_TO_SAVED, _T("&Revert to Saved"));
    menu->AppendSeparator();
    menu->AppendCheckItem(ID_AUTOLOAD, _T("Auto &Load Last File"))->Check(mAutoLoad);
    menu->AppendSeparator();
    menu->Append(wxID_CLOSE, wxT("&Close Window\tCtrl+W"));
    bar->Append(menu, wxT("&File"));

    menu = new wxMenu();
    menu->Append(wxID_UNDO, _("&Undo\tCtrl+Z"));
    menu->Append(wxID_REDO, _("&Redo\tCtrl+Y"));
    menu->AppendSeparator();
    menu->Append(wxID_CUT, _("Cu&t\tCtrl+X"));
    menu->Append(wxID_COPY, _("&Copy\tCtrl+C"));
    menu->Append(wxID_PASTE, _("&Paste\tCtrl+V"));
    menu->Append(wxID_CLEAR, _("Cle&ar\tCtrl+L"));
    menu->AppendSeparator();
    menu->Append(wxID_SELECTALL, _("Select A&ll\tCtrl+A"));
    menu->AppendSeparator();
    menu->Append(wxID_FIND, _("&Find...\tCtrl+F"));
    menu->AppendSeparator();
    wxMenu* sub = new wxMenu();
    sub->Append(ID_MATCH, _("&Matching Paren\tF8"));
    sub->Append(ID_TOP, _("&Top S-expr\tF9"));
    sub->Append(ID_UP, _("&Higher S-expr\tF10"));
    sub->Append(ID_PREVIOUS, _("&Previous S-expr\tF11"));
    sub->Append(ID_NEXT, _("&Next S-expr\tF12"));
    menu->AppendSubMenu(sub, _("&Go to"));
    menu->AppendSeparator();
    menu->AppendCheckItem(ID_AUTOWRAP, _T("Auto &Wrap"))->Check(mAutoWrap);
    bar->Append(menu, wxT("&Edit"));

    menu = new wxMenu();
    menu->Append(ID_FONT, _("Select &Font..."));
    menu->AppendSeparator();
    menu->Append(ID_SPLITV, _("Split &Vertically"));
    menu->Append(ID_SPLITH, _("Split &Horizontally"));
    menu->AppendSeparator();
    menu->AppendCheckItem(ID_TOGGLECODE, _("Show S&cript"));
    menu->AppendCheckItem(ID_TOGGLEOUTPUT, _("Show &Output"));
    menu->AppendSeparator();
    sub = new wxMenu();
    sub->AppendRadioItem(ID_LARGEICONS, _("&Large Icons"));
    sub->AppendRadioItem(ID_SMALLICONS, _("&Small Icons"));
    menu->AppendSubMenu(sub, _("Toolbar"));
    bar->Append(menu, wxT("&View"));

    menu = new wxMenu();
    menu->Append(ID_GO, _("&Go\tF5"));
    menu->Append(ID_STOP, _("&Stop\tF6"));
    bar->Append(menu, wxT("&Run"));

#if defined(__WXMAC__)
    menu->Append(wxID_ABOUT, _("&About"));
#else
    menu = new wxMenu();
    menu->Append(wxID_ABOUT, _("&About"));
    bar->Append(menu, wxT("Help"));
#endif

    SetMenuBar(bar);

    RecreateToolbar(mLargeIcons);

    wxRect r;
    r.SetX(gPrefs->Read(wxT("NyqBench/Window/X"), -1));
    r.SetY(gPrefs->Read(wxT("NyqBench/Window/Y"), -1));
    r.SetWidth(gPrefs->Read(wxT("NyqBench/Window/Width"), -1));
    r.SetHeight(gPrefs->Read(wxT("NyqBench/Window/Height"), -1));
    if (r == wxRect(-1, -1, -1, -1)) {
        Center();
    } else {
        SetSize(r);
    }

    bool maximized = false;
    gPrefs->Read(wxT("NyqBench/Window/Maximized"), maximized);
    if (maximized) {
        Maximize();
    }

    long sashpos;
    sashpos = gPrefs->Read(wxT("NyqBench/SplitX"), 0l);
    if (sashpos > 0) {
        mSplitter->SetSashPosition(sashpos);
    }

    wxString dflt = wxSystemSettings::GetFont(wxSYS_SYSTEM_FONT).GetNativeFontInfoDesc();
    wxString desc;
    wxTextAttr attr;

    desc = gPrefs->Read(wxT("NyqBench/ScriptFont"), dflt);
    mScriptFont.SetNativeFontInfo(desc);
#if defined(__WXMSW__)
    // Force SYSTEM encoding to prevent conversion to Unicode in wxTextCtrl::DoWriteText().
    // I don't know if this is something I'm doing wrong, but I'll have to look at this
    // later if I get bored.
    mScriptFont.SetEncoding(wxFONTENCODING_SYSTEM);
#endif
    attr.SetFont(mScriptFont);
    mScript->SetDefaultStyle(attr);

    desc = gPrefs->Read(wxT("NyqBench/OutputFont"), dflt);
    mOutputFont.SetNativeFontInfo(desc);
#if defined(__WXMSW__)
    // Force SYSTEM encoding to prevent conversion to Unicode in wxTextCtrl::DoWriteText().
    // I don't know if this is something I'm doing wrong, but I'll have to look at this
    // later if I get bored.
    mOutputFont.SetEncoding(wxFONTENCODING_SYSTEM);
#endif
    attr.SetFont(mOutputFont);
    mOutput->SetDefaultStyle(attr);

    if (mAutoLoad && !mPath.GetFullPath().IsEmpty()) {
        LoadFile();
    }

    SetWindowTitle();
}

NyqBench::~NyqBench()
{
}

void NyqBench::SavePrefs()
{
    gPrefs->Write(wxT("NyqBench/Window/Maximized"), IsMaximized());
    if (!IsMaximized()) {
        wxRect r = GetRect();

#if !defined(__WXMAC__)
        if (IsIconized()) {
            r = mLastSize;
        }
#endif

        gPrefs->Write(wxT("NyqBench/Window/X"), r.GetX());
        gPrefs->Write(wxT("NyqBench/Window/Y"), r.GetY());
        gPrefs->Write(wxT("NyqBench/Window/Width"), r.GetWidth());
        gPrefs->Write(wxT("NyqBench/Window/Height"), r.GetHeight());
    }

    gPrefs->Write(wxT("NyqBench/AutoLoad"), mAutoLoad);
    gPrefs->Write(wxT("NyqBench/AutoWrap"), mAutoWrap);
    gPrefs->Write(wxT("NyqBench/ScriptFont"), mScriptFont.GetNativeFontInfoDesc());
    gPrefs->Write(wxT("NyqBench/OutputFont"), mOutputFont.GetNativeFontInfoDesc());
    gPrefs->Write(wxT("NyqBench/LargeIcons"), mLargeIcons);
    gPrefs->Write(wxT("NyqBench/SplitX"), mSplitter->IsSplit() ? mSplitter->GetSashPosition() : 0);
    gPrefs->Write(wxT("NyqBench/SplitMode"), mSplitter->IsSplit() ? mSplitter->GetSplitMode() : 0);
    gPrefs->Write(wxT("NyqBench/ShowCode"), mScript->IsShown());
    gPrefs->Write(wxT("NyqBench/ShowOutput"), mOutput->IsShown());
}

void NyqBench::PopulateOrExchange(ShuttleGui& S)
{
    S.StartHorizontalLay(wxEXPAND, true);
    {
        wxPanel* scriptp;
        wxPanel* outputp;
        wxStaticBoxSizer* scripts;
        wxStaticBoxSizer* outputs;
        wxBoxSizer* bs;

        mSplitter = new wxSplitterWindow(this,
                                         wxID_ANY,
                                         wxDefaultPosition,
                                         wxDefaultSize,
                                         wxSP_LIVE_UPDATE
                                         | wxSP_3DSASH
                                         | wxSP_NOBORDER);

        scriptp = new wxPanel(mSplitter,
                              wxID_ANY,
                              wxDefaultPosition,
                              wxDefaultSize,
                              wxNO_FULL_REPAINT_ON_RESIZE
                              | wxCLIP_CHILDREN);
        bs = new wxBoxSizer(wxVERTICAL);
        scriptp->SetSizer(bs);

        mScriptBox = new wxStaticBox(scriptp,
                                     wxID_ANY,
                                     _("Script"));

        scripts = new wxStaticBoxSizer(mScriptBox,
                                       wxVERTICAL);
        bs->Add(scripts, true, wxEXPAND);

        mScript = new NyqTextCtrl(scriptp,
                                  ID_SCRIPT,
                                  wxEmptyString,
                                  wxDefaultPosition,
                                  wxDefaultSize,
                                  wxTE_RICH2 | wxTE_RICH
                                  | (mAutoWrap ? wxTE_BESTWRAP : wxTE_DONTWRAP)
                                  | wxTE_NOHIDESEL
                                  | wxTE_MULTILINE);
        scripts->Add(mScript, true, wxEXPAND);

        outputp = new wxPanel(mSplitter,
                              wxID_ANY,
                              wxDefaultPosition,
                              wxDefaultSize,
                              wxNO_FULL_REPAINT_ON_RESIZE
                              | wxCLIP_CHILDREN);
        bs = new wxBoxSizer(wxVERTICAL);
        outputp->SetSizer(bs);

        mOutputBox = new wxStaticBox(outputp,
                                     wxID_ANY,
                                     _("Output"));
        outputs = new wxStaticBoxSizer(mOutputBox,
                                       wxVERTICAL);
        bs->Add(outputs, true, wxEXPAND);

        mOutput = new NyqTextCtrl(outputp,
                                  ID_OUTPUT,
                                  wxEmptyString,
                                  wxDefaultPosition,
                                  wxDefaultSize,
                                  wxTE_READONLY |
#if !defined(__WXMAC__)
// I could not get the bloody horizontal scroll bar to appear on
// wxMac, so we can't use wxTE_DONTWRAP as you can't easily scroll
// left and right.
                                  wxTE_DONTWRAP |
#endif
                                  wxTE_NOHIDESEL
                                  | wxTE_MULTILINE);
        outputs->Add(mOutput, true, wxEXPAND);

        switch (mSplitMode) {
        case wxSPLIT_VERTICAL:
            mSplitter->SplitVertically(scriptp, outputp, 300);
            break;

        case wxSPLIT_HORIZONTAL:
            mSplitter->SplitHorizontally(scriptp, outputp, 300);
            break;

        default:
            mSplitter->Initialize((mShowCode ? scriptp : outputp));
            break;
        }

        mSplitter->SetMinimumPaneSize(50);

        S.AddSpace(5, 1);
        S.Prop(true);
        S.Position(wxEXPAND).AddWindow(mSplitter);
        S.AddSpace(5, 1);

        mSplitter->SetMinSize(wxSize(600, 400));
    }
    S.EndHorizontalLay();

    S.AddSpace(1, 5);

    return;
}

void NyqBench::OnClose(wxCloseEvent& e)
{
    if (!Validate()) {
        e.Veto();
    } else {
        Show(false);
    }
}

void NyqBench::OnMove(wxMoveEvent& e)
{
    e.Skip();
    if (!IsIconized() && !IsMaximized()) {
        mLastSize.SetPosition(e.GetPosition());
    }
}

void NyqBench::OnSize(wxSizeEvent& e)
{
    e.Skip();
    if (!IsIconized() && !IsMaximized()) {
        mLastSize.SetSize(e.GetSize());
    }
}

void NyqBench::OnCloseWindow(wxCommandEvent& e)
{
    Close();
}

void NyqBench::OnNew(wxCommandEvent& e)
{
    if (!Validate()) {
        return;
    }

    mPath.SetFullName(wxEmptyString);

    while (mScript->CanUndo()) {
        mScript->Undo();
    }

    mScript->Clear();
    mScript->DiscardEdits();

    SetWindowTitle();
}

void NyqBench::OnOpen(wxCommandEvent& e)
{
    if (mScript->IsModified() && !Validate()) {
        return;
    }

    wxFileDialog dlog(this,
                      _("Load Nyquist script"),
                      mPath.GetPath(),
                      wxEmptyString,
                      _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|All files|*"),
                      wxFD_OPEN | wxRESIZE_BORDER);

    if (dlog.ShowModal() != wxID_OK) {
        return;
    }

    mPath = dlog.GetPath();
    gPrefs->Write(wxT("NyqBench/Path"), mPath.GetFullPath());

    LoadFile();

    SetWindowTitle();
}

void NyqBench::OnSave(wxCommandEvent& e)
{
    if (mScript->GetLastPosition() == 0) {
        return;
    }

    if (mPath.GetFullPath().IsEmpty()) {
        OnSaveAs(e);
        return;
    }

    if (!mScript->SaveFile(mPath.GetFullPath())) {
        AudacityMessageBox(XO("Script was not saved."),
                           XO("Warning"),
                           wxICON_EXCLAMATION,
                           this);
        return;
    }
}

void NyqBench::OnSaveAs(wxCommandEvent& e)
{
    if (mScript->GetLastPosition() == 0) {
        return;
    }

    wxFileDialog dlog(this,
                      _("Save Nyquist script"),
                      mPath.GetFullPath(),
                      wxEmptyString,
                      _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|All files|*"),
                      wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER);

    if (dlog.ShowModal() != wxID_OK) {
        return;
    }

    mPath = dlog.GetPath();
    gPrefs->Write(wxT("NyqBench/Path"), mPath.GetFullPath());

    if (!mScript->SaveFile(mPath.GetFullPath())) {
        AudacityMessageBox(XO("Script was not saved."),
                           XO("Warning"),
                           wxICON_EXCLAMATION,
                           this);
        return;
    }

    SetWindowTitle();
}

void NyqBench::OnAutoLoad(wxCommandEvent& e)
{
    mAutoLoad = e.IsChecked();
}

void NyqBench::OnRevert(wxCommandEvent& e)
{
    if (mPath.GetFullPath().IsEmpty()) {
        return;
    }

    if (!Validate()) {
        return;
    }

    LoadFile();
}

void NyqBench::OnUndo(wxCommandEvent& e)
{
    (FindFocus() == mScript ? mScript : mOutput)->Undo();
}

void NyqBench::OnRedo(wxCommandEvent& e)
{
    (FindFocus() == mScript ? mScript : mOutput)->Redo();
}

void NyqBench::OnCut(wxCommandEvent& e)
{
    (FindFocus() == mScript ? mScript : mOutput)->Cut();
}

void NyqBench::OnCopy(wxCommandEvent& e)
{
    (FindFocus() == mScript ? mScript : mOutput)->Copy();
}

void NyqBench::OnPaste(wxCommandEvent& e)
{
    (FindFocus() == mScript ? mScript : mOutput)->Paste();
}

void NyqBench::OnClear(wxCommandEvent& e)
{
    (FindFocus() == mScript ? mScript : mOutput)->Clear();
}

void NyqBench::OnSelectAll(wxCommandEvent& e)
{
    (FindFocus() == mScript ? mScript : mOutput)->SetSelection(-1, -1);
}

void NyqBench::OnFind(wxCommandEvent& e)
{
    if (mFindDlg) {
        delete mFindDlg;
        mFindDlg = NULL;
    } else {
        NyqTextCtrl* w = (NyqTextCtrl*)FindFocus();
        if (w == mScript || w == mOutput) {
            mFindText = w;

            int flags = 0;

            flags |= (gPrefs->Read(wxT("NyqBench/Find/Down"), 0L) ? wxFR_DOWN : 0);
            flags |= (gPrefs->Read(wxT("NyqBench/Find/Word"), 0L) ? wxFR_WHOLEWORD : 0);
            flags |= (gPrefs->Read(wxT("NyqBench/Find/Case"), 0L) ? wxFR_MATCHCASE : 0);

            mFindData.SetFlags(flags);

            mFindDlg = new wxFindReplaceDialog(this,
                                               &mFindData,
                                               _("Find dialog"),
                                               wxFR_NOWHOLEWORD);
            mFindDlg->Show(true);
        }
    }
}

void NyqBench::OnGoMatch(wxCommandEvent& e)
{
    mScript->GoMatch();
}

void NyqBench::OnGoTop(wxCommandEvent& e)
{
    mScript->GoTop();
}

void NyqBench::OnGoUp(wxCommandEvent& e)
{
    mScript->GoUp();
}

void NyqBench::OnGoPrev(wxCommandEvent& e)
{
    mScript->GoPrev();
}

void NyqBench::OnGoNext(wxCommandEvent& e)
{
    mScript->GoNext();
}

void NyqBench::OnAutoWrap(wxCommandEvent& e)
{
    mAutoWrap = e.IsChecked();

    wxWindow* parent = mScript->GetParent();
    wxString text = mScript->GetValue();
    bool focused = wxWindow::FindFocus() == mScript;
    long pos = mScript->GetInsertionPoint();
    long from;
    long to;
    mScript->GetSelection(&from, &to);

    wxSizer* s = mScript->GetContainingSizer();
    s->Detach(mScript);
    delete mScript;

    mScript = new NyqTextCtrl(parent,
                              ID_SCRIPT,
                              wxEmptyString,
                              wxDefaultPosition,
                              wxDefaultSize,
                              (mAutoWrap ? wxTE_BESTWRAP : wxTE_DONTWRAP)
                              | wxTE_NOHIDESEL | wxTE_RICH2
                              | wxTE_MULTILINE);
    s->Add(mScript, 1, wxEXPAND);
    s->Layout();

    mScript->ChangeValue(text);
    mScript->SetInsertionPoint(pos);
    mScript->SetSelection(from, to);

    if (focused) {
        mScript->SetFocus();
    }
}

void NyqBench::OnFont(wxCommandEvent& e)
{
    wxWindow* w = FindFocus();
    wxFontData data;
    wxFontDialog dlg(this, data);

    if (w != mScript && w != mOutput) {
        return;
    }

    data.SetInitialFont(w == mScript ? mScriptFont : mOutputFont);

    if (dlg.ShowModal() == wxID_OK) {
        wxFontData retData = dlg.GetFontData();
        wxFont font = retData.GetChosenFont();
        wxTextAttr attr;
        attr.SetFont(font);

        if (w == mScript) {
            mScriptFont = font;
        } else {
            mOutputFont = font;
        }

        ((wxTextCtrl*)w)->SetDefaultStyle(attr);
        ((wxTextCtrl*)w)->SetStyle(0, ((wxTextCtrl*)w)->GetLastPosition(), attr);
        w->Refresh();
    }
}

void NyqBench::OnSplitV(wxCommandEvent& e)
{
    if (mSplitter->IsSplit()) {
        mSplitter->Unsplit();
    }

    mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
}

void NyqBench::OnSplitH(wxCommandEvent& e)
{
    if (mSplitter->IsSplit()) {
        mSplitter->Unsplit();
    }

    mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
}

void NyqBench::OnToggleCode(wxCommandEvent& e)
{
    if (e.IsChecked()) {
        if (mSplitter->IsSplit()) {
            // Should never happen
            return;
        }

        if (mSplitMode == wxSPLIT_VERTICAL) {
            mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
        } else {
            mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
        }
    } else {
        if (!mSplitter->IsSplit()) {
            // Should never happen
            return;
        }

        mSplitMode = mSplitter->GetSplitMode();
        mSplitter->Unsplit(mScript->GetParent());
    }
}

void NyqBench::OnToggleOutput(wxCommandEvent& e)
{
    if (e.IsChecked()) {
        if (mSplitter->IsSplit()) {
            // Should never happen
            return;
        }

        if (mSplitMode == wxSPLIT_VERTICAL) {
            mSplitter->SplitVertically(mScript->GetParent(), mOutput->GetParent());
        } else {
            mSplitter->SplitHorizontally(mScript->GetParent(), mOutput->GetParent());
        }
    } else {
        if (!mSplitter->IsSplit()) {
            // Should never happen
            return;
        }

        mSplitMode = mSplitter->GetSplitMode();
        mSplitter->Unsplit(mOutput->GetParent());
    }
}

void NyqBench::OnSmallIcons(wxCommandEvent& e)
{
    RecreateToolbar(false);
}

void NyqBench::OnLargeIcons(wxCommandEvent& e)
{
    RecreateToolbar(true);
}

void NyqBench::OnGo(wxCommandEvent& e)
{
    auto pEffect
        =std::make_unique<NyquistEffect>(L"Nyquist Effect Workbench");
    mEffect = pEffect.get();
    const PluginID& ID
        =EffectManager::Get().RegisterEffect(std::move(pEffect));

    mEffect->SetCommand(mScript->GetValue());
    mEffect->RedirectOutput();

    auto p = GetActiveProject().lock();
    wxASSERT(p);

    if (p) {
        wxWindowDisabler disable(this);
        NyqRedirector redir((NyqTextCtrl*)mOutput);

        mRunning = true;
        UpdateWindowUI();

        EffectUI::DoEffect(ID, CommandContext(*p), 0);

        mRunning = false;
        UpdateWindowUI();
    }

    Raise();

    EffectManager::Get().UnregisterEffect(ID);
}

void NyqBench::OnStop(wxCommandEvent& e)
{
    mRunning = false;
    mEffect->Stop();
}

void NyqBench::OnAbout(wxCommandEvent& e)
{
    wxAboutDialogInfo i;

    i.AddArtist(_("Harvey Lubin (logo)"));
    i.AddArtist(_("Tango Icon Gallery (toolbar icons)"));
    i.AddDeveloper(_("Leland Lucius"));
    i.SetCopyright(_("(C) 2009 by Leland Lucius"));
    i.SetDescription(_("External Audacity module which provides a simple IDE for writing effects."));
    i.SetName(_("Nyquist Effect Workbench"));
    i.SetVersion(__TDATE__);

    wxAboutBox(i);
}

void NyqBench::OnFindDialog(wxFindDialogEvent& e)
{
    wxEventType type = e.GetEventType();

    if (type == wxEVT_COMMAND_FIND_CLOSE) {
        wxFindReplaceDialog* dlg = e.GetDialog();

        dlg->Destroy();

        int flags = mFindData.GetFlags();

        gPrefs->Write(wxT("NyqBench/Find/Down"), (flags & wxFR_DOWN) != 0);
        gPrefs->Write(wxT("NyqBench/Find/Word"), (flags & wxFR_WHOLEWORD) != 0);
        gPrefs->Write(wxT("NyqBench/Find/Case"), (flags & wxFR_MATCHCASE) != 0);

        mFindDlg = NULL;
        mFindText = NULL;

        return;
    }

    wxString text = mFindText->GetValue();

#if defined(__WXMSW__)
    // We cheat on Windows.  We know that the Windows text control
    // uses CRLF for line endings and if we don't account for that,
    // the selection positions will be off.
    //
    // Not sure why I thought I needed this, but it appears not to
    // be.  Leaving just in case.
    //
    // text.Replace(wxT("\n"), wxT("\r\n"));
#endif

    size_t startpos = mFindText->GetInsertionPoint();
    size_t len = mFindText->GetLastPosition();
    size_t pos;

    wxString find = e.GetFindString();
    bool down = (e.GetFlags() & wxFR_DOWN) != 0;
    bool mixed = (e.GetFlags() & wxFR_MATCHCASE) != 0;

    if (!mixed) {
        text.MakeUpper();
        find.MakeUpper();
    }

    if (down) {
        pos = text.find(find, startpos);
        if (type == wxEVT_COMMAND_FIND_NEXT && pos == startpos && pos < len) {
            pos = text.find(find, startpos + 1);
        }
    } else {
        pos = text.rfind(find, startpos);
        if (type == wxEVT_COMMAND_FIND_NEXT && pos == startpos && pos > 0) {
            pos = text.rfind(find, startpos - 1);
        }
    }

    if (pos == wxString::npos) {
        AudacityMessageBox(XO("No matches found"),
                           XO("Nyquist Effect Workbench"),
                           wxOK | wxCENTER,
                           e.GetDialog());

        return;
    }

    mFindText->SetInsertionPoint((long)pos);

#if defined(__WXGTK__)
    // GTK's selection and intertion pointer interact where the insertion
    // pointer winds up after the second parameter, so we reverse them to
    // force the pointer at the beginning of the selection.  Doing so
    // allows reverse find to work properly.
    mFindText->SetSelection((long)(pos + find.Length()), (long)pos);
#else
    mFindText->SetSelection((long)pos, (long)(pos + find.Length()));
#endif

#if defined(__WXMAC__)
    // Doing this coaxes the text control to update the selection.  Without
    // it the selection doesn't appear to change if the found string is within
    // the currently displayed text, i.e., no reposition is needed.
    mFindText->Show(false);
    mFindText->Show(true);
#endif
}

void NyqBench::OnTextUpdate(wxCommandEvent& e)
{
    // This really shouldn't be necessary, but Paste()ing doesn't mark the
    // control as dirty...at least on the Mac.
    ((NyqTextCtrl*)e.GetEventObject())->MarkDirty();
}

void NyqBench::OnMenuUpdate(wxUpdateUIEvent& e)
{
    if (e.GetId() != wxID_REVERT_TO_SAVED) {
        e.Enable((mScript->GetLastPosition() > 0) || mScript->IsModified());
    } else {
        e.Enable(mScript->IsModified());
    }
}

void NyqBench::OnUndoUpdate(wxUpdateUIEvent& e)
{
    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanUndo());
}

void NyqBench::OnRedoUpdate(wxUpdateUIEvent& e)
{
    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanRedo());
}

void NyqBench::OnCutUpdate(wxUpdateUIEvent& e)
{
    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanCut());
}

void NyqBench::OnCopyUpdate(wxUpdateUIEvent& e)
{
    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanCopy());
}

void NyqBench::OnPasteUpdate(wxUpdateUIEvent& e)
{
    e.Enable((FindFocus() == mScript ? mScript : mOutput)->CanPaste());
}

void NyqBench::OnClearUpdate(wxUpdateUIEvent& e)
{
    e.Enable(FindFocus() == mOutput ? true : false);
}

void NyqBench::OnViewUpdate(wxUpdateUIEvent& e)
{
    wxMenuBar* bar = GetMenuBar();
    bar->Enable(ID_SPLITV, !mSplitter->IsSplit() || mSplitter->GetSplitMode() != wxSPLIT_VERTICAL);
    bar->Enable(ID_SPLITH, !mSplitter->IsSplit() || mSplitter->GetSplitMode() != wxSPLIT_HORIZONTAL);
    bar->Check(ID_TOGGLECODE, mScript->GetParent()->IsShown());
    bar->Check(ID_TOGGLEOUTPUT, mOutput->GetParent()->IsShown());
    bar->Check(ID_LARGEICONS, mLargeIcons);
    bar->Check(ID_SMALLICONS, !mLargeIcons);
}

void NyqBench::OnRunUpdate(wxUpdateUIEvent& e)
{
    auto p = GetActiveProject().lock();
    wxToolBar* tbar = GetToolBar();
    wxMenuBar* mbar = GetMenuBar();

    auto gAudioIO = AudioIOBase::Get();
    if (p && gAudioIO->IsBusy()) {
        mbar->Enable(ID_GO, false);
        mbar->Enable(ID_STOP, false);

        tbar->EnableTool(ID_GO, false);
        tbar->EnableTool(ID_STOP, false);
    } else {
        mbar->Enable(ID_GO, (mScript->GetLastPosition() > 0) && !mRunning);
        mbar->Enable(ID_STOP, (mScript->GetLastPosition() > 0) && mRunning);

        tbar->EnableTool(ID_GO, (mScript->GetLastPosition() > 0) && !mRunning);
        tbar->EnableTool(ID_STOP, (mScript->GetLastPosition() > 0) && mRunning);
    }
}

void NyqBench::OnScriptUpdate(wxUpdateUIEvent& e)
{
    if (mScriptBox && mScript && FindFocus() == mScript) {
        wxString label = mScriptBox->GetLabel();
        if (label == _("Script")) {
            label += wxT("*");
            mScriptBox->SetLabel(label);
            mOutputBox->SetLabel(_("Output"));
        }
    }
}

void NyqBench::OnOutputUpdate(wxUpdateUIEvent& e)
{
    if (mOutputBox && mOutput && FindFocus() == mOutput) {
        wxString label = mOutputBox->GetLabel();
        if (label == _("Output")) {
            label += wxT("*");
            mOutputBox->SetLabel(label);
            mScriptBox->SetLabel(_("Script"));
        }
    }
}

bool NyqBench::Validate()
{
    if (mScript->GetLastPosition() > 0 && mScript->IsModified()) {
        int ans;
        ans = AudacityMessageBox(XO("Code has been modified. Are you sure?"),
                                 XO("Warning"),
                                 wxYES_NO | wxICON_QUESTION,
                                 this);
        if (ans == wxNO) {
            return false;
        }
    }

    return true;
}

void NyqBench::SetWindowTitle()
{
    wxString name = _("Untitled");

    if (!mPath.GetFullPath().IsEmpty()) {
        name = mPath.GetFullName();
    }

    SetTitle(_("Nyquist Effect Workbench - ") + name);
}

void NyqBench::RecreateToolbar(bool large)
{
    mLargeIcons = large;

    wxToolBar* tb = GetToolBar();
    if (tb) {
        delete tb;
    }
    tb = CreateToolBar();

    wxSize sz;

    if (!mLargeIcons) {
        tb->SetToolBitmapSize(wxSize(16, 16));
        mPics[0] = wxBitmap(document_new_small);
        mPics[1] = wxBitmap(document_open_small);
        mPics[2] = wxBitmap(document_save_as_small);
        mPics[3] = wxBitmap(document_save_small);
        mPics[4] = wxBitmap(edit_copy_small);
        mPics[5] = wxBitmap(edit_cut_small);
        mPics[6] = wxBitmap(edit_paste_small);
        mPics[7] = wxBitmap(edit_clear_small);
        mPics[8] = wxBitmap(edit_delete_small);
        mPics[9] = wxBitmap(edit_select_all_small);
        mPics[10] = wxBitmap(edit_undo_small);
        mPics[11] = wxBitmap(edit_redo_small);
        mPics[12] = wxBitmap(edit_find_small);
        mPics[13] = wxBitmap(system_search_small);
        mPics[14] = wxBitmap(go_top_small);
        mPics[15] = wxBitmap(go_up_small);
        mPics[16] = wxBitmap(go_previous_small);
        mPics[17] = wxBitmap(go_next_small);
        mPics[18] = wxBitmap(media_playback_start_small);
        mPics[19] = wxBitmap(media_playback_stop_small);
    } else {
        tb->SetToolBitmapSize(wxSize(32, 32));
        mPics[0] = wxBitmap(document_new_large);
        mPics[1] = wxBitmap(document_open_large);
        mPics[2] = wxBitmap(document_save_as_large);
        mPics[3] = wxBitmap(document_save_large);
        mPics[4] = wxBitmap(edit_copy_large);
        mPics[5] = wxBitmap(edit_cut_large);
        mPics[6] = wxBitmap(edit_paste_large);
        mPics[7] = wxBitmap(edit_clear_large);
        mPics[8] = wxBitmap(edit_delete_large);
        mPics[9] = wxBitmap(edit_select_all_large);
        mPics[10] = wxBitmap(edit_undo_large);
        mPics[11] = wxBitmap(edit_redo_large);
        mPics[12] = wxBitmap(edit_find_large);
        mPics[13] = wxBitmap(system_search_large);
        mPics[14] = wxBitmap(go_top_large);
        mPics[15] = wxBitmap(go_up_large);
        mPics[16] = wxBitmap(go_previous_large);
        mPics[17] = wxBitmap(go_next_large);
        mPics[18] = wxBitmap(media_playback_start_large);
        mPics[19] = wxBitmap(media_playback_stop_large);
    }

    tb->SetMargins(2, 2);

    tb->AddTool(wxID_NEW, _("New"), mPics[0], _("New script"));
    tb->AddTool(wxID_OPEN, _("Open"), mPics[1], _("Open script"));
    tb->AddTool(wxID_SAVE, _("Save"), mPics[2], _("Save script"));
    tb->AddTool(wxID_SAVEAS, _("Save As"), mPics[3], _("Save script as..."));
    tb->AddSeparator();
    tb->AddTool(wxID_COPY, _("Copy"), mPics[4], _("Copy to clipboard"));
    tb->AddTool(wxID_CUT, _("Cut"), mPics[5], _("Cut to clipboard"));
    tb->AddTool(wxID_PASTE, _("Paste"), mPics[6], _("Paste from clipboard"));
    tb->AddTool(wxID_CLEAR, _("Clear"), mPics[7], _("Clear selection"));
    tb->AddTool(wxID_SELECTALL, _("Select All"), mPics[9], _("Select all text"));
    tb->AddSeparator();
    tb->AddTool(wxID_UNDO, _("Undo"), mPics[10], _("Undo last change"));
    tb->AddTool(wxID_REDO, _("Redo"), mPics[11], _("Redo previous change"));
    tb->AddSeparator();
    tb->AddTool(wxID_FIND, _("Find"), mPics[12], _("Find text"));
    tb->AddSeparator();
    tb->AddTool(ID_MATCH, _("Match"), mPics[13], _("Go to matching paren"));
    tb->AddTool(ID_TOP, _("Top"), mPics[14], _("Go to top S-expr"));
    tb->AddTool(ID_UP, _("Up"), mPics[15], _("Go to higher S-expr"));
    tb->AddTool(ID_PREVIOUS, _("Previous"), mPics[16], _("Go to previous S-expr"));
    tb->AddTool(ID_NEXT, _("Next"), mPics[17], _("Go to next S-expr"));
    tb->AddSeparator();
    tb->AddTool(ID_GO, _("Start"), mPics[18], _("Start script"));
    tb->AddTool(ID_STOP, _("Stop"), mPics[19], _("Stop script"));

    tb->Realize();

    Layout();
    Fit();
    SetMinSize(GetSize());
}

void NyqBench::LoadFile()
{
    wxString path = mPath.GetFullPath();

    if (path.IsEmpty()) {
        return;
    }

    wxFFile f(path);
    if (f.IsOpened()) {
        wxString t;
        if (f.ReadAll(&t)) {
//#if defined(__WXGTK__) || defined(__WXMAC__)
            t.Replace(wxT("\r\n"), wxT("\n"));
            t.Replace(wxT("\r"), wxT("\n"));
//#elif defined(__WXMSW__)
//         t.Replace("\r\n", "\n");
//#endif
            mScript->SetValue(t);
            mScript->DiscardEdits();
        }
    }

//   mScript->LoadFile(mPath.GetFullPath());
}

//----------------------------------------------------------------------------
// Connects Audacity menu item to an action in this dll.
// Only one action implemented so far.
//----------------------------------------------------------------------------
void NyqBench::ShowNyqBench(const CommandContext&)
{
    Show();
}
